package com.young.statemachine.config;

import com.young.statemachine.domain.Work;
import com.young.statemachine.enums.WorkEvents;
import com.young.statemachine.enums.WorkStates;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

/**
 * @className: StateMachineConfig
 * @author: Young
 * @date: 2025/1/15 22:38
 * @Version: 1.0
 * @description: 状态机配置类
 */
@Configuration
@EnableStateMachine
public class StateMachineConfig extends StateMachineConfigurerAdapter<WorkStates, WorkEvents> {

    private static final Logger log = LoggerFactory.getLogger(StateMachineConfig.class);

    @Override
    public void configure(StateMachineConfigurationConfigurer<WorkStates, WorkEvents> config) throws Exception {
        config.withConfiguration()
                .autoStartup(true)
                .listener(listener());
    }

    @Override
    public void configure(StateMachineStateConfigurer<WorkStates, WorkEvents> states) throws Exception {
        states
                .withStates()
                .initial(WorkStates.UNREACHED)
                .end(WorkStates.RELEASED)
                .states(EnumSet.allOf(WorkStates.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<WorkStates, WorkEvents> transitions) throws Exception {
        transitions
                .withExternal()
                .source(WorkStates.UNREACHED).target(WorkStates.REACHED).event(WorkEvents.REACHED)
                .and()
                .withExternal()
                .source(WorkStates.REACHED).target(WorkStates.SCANNED).event(WorkEvents.SCAN_OK)
                .and()
                .withExternal()
                .source(WorkStates.SCANNED).target(WorkStates.TESTED).event(WorkEvents.TESTED)
                .and()
                .withExternal()
                .source(WorkStates.TESTED).target(WorkStates.RELEASED).event(WorkEvents.RELEASED)
                .and()
                .withExternal()
                .source(WorkStates.SCANNED).target(WorkStates.RELEASED).event(WorkEvents.RELEASED);
    }

    @Bean
    public StateMachineListener<WorkStates, WorkEvents> listener() {
        return new StateMachineListenerAdapter<WorkStates, WorkEvents>() {
            @Override
            public void stateChanged(State<WorkStates, WorkEvents> from, State<WorkStates, WorkEvents> to) {
                log.info("state change to :{}", to.getId());
            }

            @Override
            public void stateMachineError(StateMachine<WorkStates, WorkEvents> stateMachine, Exception exception) {
                log.error("exception caught :{}", exception.getMessage());
            }

            @Override
            public void eventNotAccepted(Message<WorkEvents> event) {
               Work work = (Work) event.getHeaders().get("work");
               log.error("event not accepted :{}", work);
            }
        };
    }

    @Bean
    public StateMachinePersist<WorkStates, WorkEvents,String> inMemoryStateMachinePersist() {
        return new StateMachinePersist<>() {
            private final Map<String,StateMachineContext<WorkStates, WorkEvents>> contexts = new HashMap<>();
            @Override
            public void write(StateMachineContext<WorkStates, WorkEvents> context, String contextObj) throws Exception {
                contexts.put(contextObj,context);
            }

            @Override
            public StateMachineContext<WorkStates, WorkEvents> read(String contextObj) throws Exception {
                return contexts.get(contextObj);
            }
        };
    }
}
